﻿Imports System.IO
Imports System.IO.Path
Imports System.Drawing

Namespace Items

    ''' <summary>
    ''' Represents a PowerSong play list, which in turn comprises zero or more items that can be projected.
    ''' </summary>
    Public Class PlayList
        Implements IEnumerable

        Private Const PLAYLIST_VERSION As Integer = 20100131

        Private FItems As New List(Of PlayListItem)

        ''' <summary>
        ''' Adds the specified item to the play list.
        ''' </summary>
        ''' <param name="item">The item to add to the play list.</param>
        Public Sub Add(ByVal item As PlayListItem)
            FItems.Add(item)
        End Sub

        ''' <summary>
        ''' Returns the number of items contained in this play list.
        ''' </summary>
        ''' <returns>The number of items in the play list.</returns>
        Public Function Count() As Integer
            Return FItems.Count
        End Function

        ''' <summary>
        ''' Removes the specified item from the play list.
        ''' </summary>
        ''' <param name="item">The item to remove from the play list. It must exist in the list.</param>
        Public Sub Remove(ByVal item As PlayListItem)
            FItems.Remove(item)
        End Sub

        ''' <summary>
        ''' Gets or sets the items in the play list, accessing by index.
        ''' </summary>
        ''' <value>The items in the play list.</value>
        Default Public Property Items(ByVal index As Integer) As PlayListItem
            Get
                Return FItems(index)
            End Get
            Set(ByVal value As PlayListItem)
                FItems(index) = value
            End Set
        End Property

        ''' <summary>
        ''' Loads the given play list.
        ''' </summary>
        ''' <param name="database">The database the play list refers to.</param>
        ''' <param name="fileName">The file name of the playlist to load.</param>
        ''' <param name="allSongsFound">This is set to True only if all songs referenced in the play list file are contained in the given database.</param>
        Public Shared Function Load(ByVal database As Database, _
                                    ByVal fileName As String, _
                                    ByRef allSongsFound As Boolean) As PlayList

            If GetExtension(fileName).ToUpper = ".PSPL" Then
                Return LoadClassic(database, fileName, allSongsFound)
            Else
                Return LoadModern(database, fileName, allSongsFound)
            End If

        End Function

        ''' <summary>
        ''' Loads the given classic playlist file.
        ''' </summary>
        ''' <param name="database">The database the play list refers to.</param>
        ''' <param name="fileName">The playlist filename.</param>
        Private Shared Function LoadClassic(ByVal database As Database, _
                                            ByVal fileName As String, _
                                            ByRef allSongsFound As Boolean) As PlayList

            ' Get all songs in the database
            allSongsFound = True
            Dim Result As New PlayList
            Dim SongIDs As List(Of Guid) = database.Songs.GetAllSongIDs

            ' Load the play list
            Dim File As New BinaryReader(New FileStream(fileName, FileMode.Open))
            Try

                While File.BaseStream.Position < File.BaseStream.Length

                    ' Add the song to the live playlist
                    Dim NextSongID As Guid = New Guid(File.ReadString)
                    If SongIDs.Contains(NextSongID) Then
                        Dim Song As SongItem = database.Songs.GetSong(NextSongID)
                        Result.Add(New PlayListItem(Song, database.Styles.GetDefaultStyle))
                    Else
                        allSongsFound = False
                    End If

                End While

            Finally
                File.Close()
            End Try

            ' Return the play list
            Return Result

        End Function

        ''' <summary>
        ''' Loads the given modern playlist file.
        ''' </summary>
        ''' <param name="database">The database the play list refers to.</param>
        ''' <param name="fileName">The playlist filename.</param>
        Private Shared Function LoadModern(ByVal database As Database, _
                                           ByVal fileName As String, _
                                           ByRef allSongsFound As Boolean) As PlayList

            allSongsFound = True
            Dim Result As New PlayList

            ' Load the songs in the playlist
            Dim SongIDs As List(Of Guid) = database.Songs.GetAllSongIDs
            Dim File As New BinaryReader(New FileStream(fileName, FileMode.Open))

            Try

                ' Check version
                Dim Version As Integer = File.ReadInt32
                Select Case Version

                    Case PLAYLIST_VERSION
                        While File.BaseStream.Position < File.BaseStream.Length

                            ' Determine the type of item
                            Dim NextItem As PlayListItem = Nothing
                            Dim ItemType As String = File.ReadString
                            Select Case ItemType

                                Case "SongItem"
                                    Dim SongID As New Guid(File.ReadString)
                                    Dim Title As String = File.ReadString
                                    Dim StyleID As New Guid(File.ReadString)
                                    Dim CopyrightLine As String = File.ReadString

                                    ' Add the song to the playlist
                                    If SongIDs.Contains(SongID) Then
                                        NextItem = New PlayListItem(database.Songs.GetSong(SongID), _
                                                                    database.Styles.TryGetStyle(StyleID, database.Styles.GetDefaultStyle))
                                    Else
                                        allSongsFound = False
                                    End If

                                Case "AdhocItem"
                                    Dim AdhocItem As New AdhocItem
                                    AdhocItem.Title = File.ReadString
                                    AdhocItem.Parts = AdhocItem.SplitIntoParts(File.ReadString.Split(Environment.NewLine + Environment.NewLine))
                                    Dim StyleID As New Guid(File.ReadString)
                                    AdhocItem.CopyrightLine = File.ReadString
                                    NextItem = New PlayListItem(AdhocItem, database.Styles.TryGetStyle(StyleID, database.Styles.GetDefaultStyle))

                                Case Else
                                    Throw New Exception("An unknown type of item is contained in the play list.")

                            End Select

                            ' Has the background been overridden?
                            If File.ReadBoolean = True Then
                                Dim Type As Style.EBackgroundType = [Enum].Parse(GetType(Style.EBackgroundType), File.ReadString)
                                Dim Value As String = File.ReadString
                                If NextItem IsNot Nothing Then
                                    Select Case Type
                                        Case Style.EBackgroundType.Image
                                            NextItem.OverrideBackground = New OverrideBackground(Type, Value)
                                        Case Style.EBackgroundType.SolidColour
                                            NextItem.OverrideBackground = New OverrideBackground(Type, Color.FromArgb(Convert.ToInt32(Value)))
                                        Case Else
                                            Throw New Exception("An unknown type of overriding background is contained in the play list.")
                                    End Select
                                End If

                            End If

                            ' Add the item to the list
                            If NextItem IsNot Nothing Then Result.Add(NextItem)

                        End While

                    Case Else
                        If Version > PLAYLIST_VERSION Then
                            Throw New Exception("The specified playlist was created in a later version of PowerSong and therefore cannot be loaded.")
                        Else
                            Throw New Exception("The specified playlist was created in an early version of PowerSong and therefore cannot be loaded.")
                        End If

                End Select

            Finally
                File.Close()
            End Try

            Return Result

        End Function

        ''' <summary>
        ''' Saves the play list to the specified file name.
        ''' </summary>
        ''' <param name="fileName">
        ''' Name of the file to save to.
        ''' If the extension of the file is "pspl", then the format of the play list will be the classic "simple" type.
        ''' </param>
        Public Sub Save(ByVal fileName As String)

            If GetExtension(fileName).ToUpper = ".PSPL" Then
                SaveClassic(fileName)
            Else
                SaveModern(fileName)
            End If

        End Sub

        ''' <summary>
        ''' Saves the given playlist file the "old" way, which simply creates a file of guids.
        ''' </summary>
        ''' <param name="fileName">The playlist filename.</param>
        Private Sub SaveClassic(ByVal fileName As String)

            Dim File As New BinaryWriter(New FileStream(fileName, FileMode.Create))
            Try

                ' Simply write the Guid of each referenced song in the list
                For Each Item As PlayListItem In FItems
                    If TypeOf Item.Item Is SongItem Then
                        File.Write(DirectCast(Item.Item, SongItem).SongID.ToString)
                    End If
                Next

            Finally
                File.Close()
            End Try

        End Sub

        ''' <summary>
        ''' Saves the given playlist file using the "new" method, which includes the style selected as
        ''' well as playlist version management.
        ''' </summary>
        ''' <param name="fileName">The playlist filename.</param>
        Private Sub SaveModern(ByVal fileName As String)

            ' Write the current playlist version
            Dim File As New BinaryWriter(New FileStream(fileName, FileMode.Create))
            Try
                File.Write(PLAYLIST_VERSION)

                ' Save all the items in the playlist
                For Each Item As PlayListItem In FItems

                    Dim ItemWritten As Boolean = False
                    If TypeOf Item.Item Is SongItem Then

                        ' Save the song entry
                        File.Write("SongItem")
                        Dim Song As SongItem = DirectCast(Item.Item, SongItem)
                        File.Write(Song.SongID.ToString)
                        File.Write(Item.Item.Title)
                        ItemWritten = True

                    ElseIf TypeOf Item.Item Is AdhocItem Then

                        ' Save the adhoc entry
                        File.Write("AdhocItem")
                        Dim Adhoc As AdhocItem = DirectCast(Item.Item, AdhocItem)
                        File.Write(Adhoc.Title)
                        File.Write(Adhoc.CombineIntoString())
                        ItemWritten = True

                    End If

                    ' Write common data
                    If ItemWritten Then
                        File.Write(Item.Style.StyleID.ToString)
                        File.Write(Item.Item.CopyrightLine)

                        ' Write overwritten background if one is available
                        If Item.OverrideBackground IsNot Nothing Then
                            File.Write(True)
                            File.Write(Item.OverrideBackground.Type.ToString)
                            Select Case Item.OverrideBackground.Type
                                Case Style.EBackgroundType.Image
                                    File.Write(Item.OverrideBackground.Value.ToString)
                                Case Style.EBackgroundType.SolidColour
                                    File.Write(DirectCast(Item.OverrideBackground.Value, Color).ToArgb.ToString)
                                Case Else
                                    Throw New Exception("An unknown type of overriding background has been applied to one of the items in the list.")
                            End Select

                        Else
                            File.Write(False)
                        End If

                    End If

                Next

            Finally
                File.Close()
            End Try

        End Sub

        ''' <summary>
        ''' Returns an enumerator that iterates through the collection of play list items.
        ''' </summary>
        ''' <returns>
        ''' An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
        ''' </returns>
        Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
            Return New PlayListEnumerator(Me)
        End Function

    End Class

End Namespace